package com.ddz.util;

import java.util.*;

import com.ddz.model.Poker;
import com.ddz.model.enums.PokerType;
import org.json.JSONArray;

import java.util.stream.Collectors;

/**
 * @description: 扑克牌校验的工具类
 */
public class PokerUtil {

    /**
     * JsonArray转换成扑克列表
     *
     * @param jsonArray
     * @return
     */
    public static List<Poker> JsonArrayTolistPoker(JSONArray jsonArray){
        List<Poker> pokers = new ArrayList<>();
        for (int i = 0; i < jsonArray.length(); i++){
            Poker poker = new Poker(jsonArray.getString(i));
            pokers.add(poker);
        }
        return pokers;
    }

    /**
     * 检查出手牌合法性
     *
     * @param handPokers 出手牌
     * @return 结果
     */
    public static boolean checkHandPokers(List<Poker> handPokers) {
        System.out.println("正在检查出牌合法性...");

        if (handPokers.isEmpty()) return false;
        List<Integer> handPokerValueList = getHandPokerValueList(handPokers);
        // 2.根据list的size，判断出牌的类型
        int size = handPokers.size();
        switch (size) {
            case 1:
                // 单牌
                return true;
            case 2:
                // 对子/王炸
                return isPair(handPokerValueList) || isKingBomb(handPokerValueList);
            case 3:
                // 三张
                return isThreeOfOneKind(handPokerValueList);
            case 4:
                // 炸弹/三带一
                return isBomb(handPokerValueList) || isThreeOfOneKindWithOne(handPokerValueList);
            case 5:
                // 顺子/三带二
                return isStraight(handPokerValueList) || isThreeOfOneKindWithPair(handPokerValueList);
            case 6:
                // 顺子/连对/四带二
                return isStraight(handPokerValueList) || isThreePairs(handPokerValueList) || isBombWithTwo(handPokerValueList) || isPlane(handPokerValueList);
            default:
                // 长顺子、连对、飞机(两组或更多的连续三张牌)、飞机带翅膀（飞机+相同组数的单牌huo对排）
                return isStraight(handPokerValueList) || isThreePairs(handPokerValueList) || isPlane(handPokerValueList);
        }
    }

    /**
     * 获取手牌的牌型
     *
     * @param handPokers 手牌
     * @return 牌形String
     */
    public static PokerType getPokerType(List<Poker> handPokers) {
        if (handPokers == null || handPokers.isEmpty()) {
            return PokerType.INVALID; // 无效牌型
        }

        // 将牌值按从小到大排序，忽略花色
        List<Integer> values = getHandPokerValueList(handPokers);

        int size = values.size();

        switch (size) {
            case 1:
                return PokerType.SINGLE; // 单牌
            case 2:
                if (isPair(values)) return PokerType.PAIR; // 对子
                if (isKingBomb(values)) return PokerType.KING_BOMB; // 王炸
                break;
            case 3:
                if (isThreeOfOneKind(values)) return PokerType.THREE; // 三张
                break;
            case 4:
                if (isBomb(values)) return PokerType.BOMB; // 炸弹
                if (isThreeOfOneKindWithOne(values)) return PokerType.THREE_WITH_ONE; // 三带一
                break;
            case 5:
                if (isStraight(values)) return PokerType.STRAIGHT; // 顺子
                if (isThreeOfOneKindWithPair(values)) return PokerType.THREE_WITH_PAIR; // 三带二
                break;
            case 6:
                if (isStraight(values)) return PokerType.STRAIGHT; // 顺子
                if (isThreePairs(values)) return PokerType.THREE_PAIRS; // 连对
                if (isBombWithTwo(values)) return PokerType.BOMB_WITH_TWO; // 四带二
                if (isPlane(values)) return PokerType.PLANE; // 飞机
                break;
            default:
                if (isStraight(values)) return PokerType.STRAIGHT; // 长顺子
                if (isThreePairs(values)) return PokerType.THREE_PAIRS; // 连对
                if (isPlane(values)) return PokerType.PLANE; // 飞机
                break;
        }
        return PokerType.UNDEFINED; // 无法识别的牌型
    }

    /**
     * 比较出牌的牌值是否正确
     *
     * @param handPokerValueList 手牌价值
     * @param lastPokerValueList 上次牌价值
     * @param handPokerType      手牌类型
     * @param lastPokerType      上次牌类型
     * @return boolean
     */
    public static boolean comparePokerValue(List<Integer> handPokerValueList,
                                            List<Integer> lastPokerValueList,
                                            PokerType handPokerType,
                                            PokerType lastPokerType) {
        // 1.是否是炸弹
        // 1.1王炸
        if (handPokerType.equals(PokerType.KING_BOMB)) {
            // 发送王炸音乐
            return true;
        }
        // 1.2普通炸弹
        if (handPokerType.equals(PokerType.BOMB)) {
            // 如果上次保存的是普通炸弹
            if (lastPokerType.equals(PokerType.BOMB)) {
                // 比较牌值
                return lastPokerValueList.get(0) < handPokerValueList.get(0);
            }
            // 1.2.2炸弹
            return true;
        }
        // 2.验证是否属于相同的牌型
        if (!lastPokerType.equals(handPokerType)) return false;
        // 3.比较相同牌型的大小
        switch (handPokerType) {
            case SINGLE:
            case PAIR:
            case THREE:
                return lastPokerValueList.get(0) < handPokerValueList.get(0);
            case STRAIGHT:
            case THREE_PAIRS:
                if (lastPokerValueList.size() != handPokerValueList.size())
                    return false;
                // 出的数量一样比较第一张牌大小
                return lastPokerValueList.get(0) < handPokerValueList.get(0);
            case THREE_WITH_ONE:
            case THREE_WITH_PAIR:
            case PLANE:
                // 如果飞机牌(不管带不带翅膀)的数量不同，则无法出牌
                if (handPokerType.equals(PokerType.PLANE)) {
                    if (lastPokerValueList.size() != handPokerValueList.size()) return false;
                }
                // 如果为3带2，需要检验2是否为一对
                if (handPokerType.equals(PokerType.THREE_WITH_PAIR)) {
                    List<Integer> pairCards = getThreeOrFourPokersList(handPokerValueList, 2);
                    if (pairCards.size() == 2) {
                        return Objects.equals(pairCards.get(0), pairCards.get(1));
                    }
                }
                // 只需要比较数量为3张牌的值
                List<Integer> threeCards = getThreeOrFourPokersList(handPokerValueList, 3);
                // 如果找不到三张相同的牌，说明牌型不合法，返回false
                if (threeCards.isEmpty()) {
                    return false;
                }
                List<Integer> threeLastCards = getThreeOrFourPokersList(lastPokerValueList, 3);
                // 返回三张相同的牌中最大的值（即主牌值）
                return Collections.max(threeCards) > Collections.max(threeLastCards);
            case BOMB_WITH_TWO:
                if (lastPokerValueList.size() != handPokerValueList.size()) return false;
                // 需要检验2是否为一对
                List<Integer> pairCards = getThreeOrFourPokersList(handPokerValueList, 2);
                if (pairCards.size() == 2) {
                    return Objects.equals(pairCards.get(0), pairCards.get(1));
                }
                // 只需要比较数量为3张牌的值
                List<Integer> fourCards = getThreeOrFourPokersList(handPokerValueList, 4);
                // 如果找不到三张相同的牌，说明牌型不合法，返回false
                if (fourCards.isEmpty()) {
                    return false;
                }
                List<Integer> fourLastCards = getThreeOrFourPokersList(lastPokerValueList, 4);
                return Collections.max(fourCards) > Collections.max(fourLastCards);
            default:
                return false;
        }
    }

    /**
     * 获取三张或四张牌
     *
     * @param lastPokerValueList 上次出牌的结果
     * @param count              3/4
     * @return list
     */
    private static List<Integer> getThreeOrFourPokersList(List<Integer> lastPokerValueList, int count) {
        Map<Integer, Integer> countLastMap = new HashMap<>();
        for (int num : lastPokerValueList) {
            countLastMap.put(num, countLastMap.getOrDefault(num, 0) + 1);
        }
        return countLastMap.entrySet().stream()
                .filter(entry -> entry.getValue() == count)  // 找出出现次数为3的牌
                .map(Map.Entry::getKey)
                .collect(Collectors.toList());
    }


    public static List<Integer> getHandPokerValueList(List<Poker> handPokers) {
        List<Integer> handPokerValueList = new ArrayList<>();
        // 1.将出手牌转换成每张牌对应的价值，忽略花色
        for (Poker handPoker : handPokers) {
            String pokerValue = handPoker.getValue();
            int value = Integer.parseInt(pokerValue.split("-")[1]);
            if (value >= 3 && value <= 13) handPokerValueList.add(value);
            else {
                if (value >= 1 && value < 3) {
                    // 判断joker
                    int isJoker = Integer.parseInt(pokerValue.split("-")[0]);
                    if (isJoker == 5) {
                        handPokerValueList.add(value + 100);
                    } else {
                        handPokerValueList.add(value + 13);
                    }
                }
            }
        }
        // 排序
        handPokerValueList.sort(Integer::compareTo);
        return handPokerValueList;
    }


    private static boolean isKingBomb(List<Integer> list) {
        for (Integer i : list) if (i < 100) return false;
        return true;
    }

    private static boolean isPlane(List<Integer> list) {
        Map<Integer, Integer> countMap = new HashMap<>();
        for (int num : list) {
            countMap.put(num, countMap.getOrDefault(num, 0) + 1);
        }
        // 主体部分：找出所有数量为 3 的牌
        List<Integer> triples = countMap.entrySet().stream()
                .filter(entry -> entry.getValue() >= 3)
                .map(Map.Entry::getKey)
                .sorted()
                .collect(Collectors.toList());
        if (triples.size() < 2) return false; // 至少两组三张牌

        // 判断三张牌的点数是否连续
        for (int i = 0; i < triples.size() - 1; i++) {
            if (triples.get(i) + 1 != triples.get(i + 1)) return false;
        }

        // 附加部分：验证剩余牌是否符合单翅膀或双翅膀规则
        int addition = list.size() - triples.size() * 3; // 翅膀数量
        return addition == triples.size() || addition == triples.size() * 2;
    }


    private static boolean isBombWithTwo(List<Integer> list) {
        // 四张相同的牌可以在前、中、后，枚举三种可能性
        boolean case1 = Objects.equals(list.get(0), list.get(1)) &&
                Objects.equals(list.get(1), list.get(2)) &&
                Objects.equals(list.get(2), list.get(3));
        boolean case2 = Objects.equals(list.get(1), list.get(2)) &&
                Objects.equals(list.get(2), list.get(3)) &&
                Objects.equals(list.get(3), list.get(4));
        boolean case3 = Objects.equals(list.get(2), list.get(3)) &&
                Objects.equals(list.get(3), list.get(4)) &&
                Objects.equals(list.get(4), list.get(5));

        // 至少满足一种情况即为“四带二”
        return case1 || case2 || case3;
    }

    private static boolean isThreePairs(List<Integer> list) {
        int size = list.size();
        // 连对必须是偶数张牌，并且至少两对（4张牌）
        if (size < 4 || size % 2 != 0) return false;

        for (int i = 0; i < size; i += 2) {
            // 每两张牌必须相等，形成对子
            if (!list.get(i).equals(list.get(i + 1))) return false;
            // 从第二对开始，每对的第一张必须比上一对的第一张大1
            if (i >= 2 && list.get(i) != list.get(i - 2) + 1) return false;
        }
        return true;
    }

    private static boolean isThreeOfOneKindWithPair(List<Integer> list) {
        return (list.get(0).equals(list.get(1)) && list.get(1).equals(list.get(2)) && list.get(3).equals(list.get(4))) ||
                (list.get(0).equals(list.get(1)) && list.get(2).equals(list.get(3)) && list.get(3).equals(list.get(4)));
    }

    private static boolean isStraight(List<Integer> list) {
        for (int i = 0; i < list.size() - 1; i++) {
            int previousValue = list.get(i);
            int nextValue = list.get(i + 1);
            if (previousValue + 1 != nextValue) return false;
        }
        return true;
    }

    private static boolean isThreeOfOneKindWithOne(List<Integer> list) {
        return (Objects.equals(list.get(0), list.get(1)) && Objects.equals(list.get(1), list.get(2)))
                || (Objects.equals(list.get(1), list.get(2)) && Objects.equals(list.get(2), list.get(3)));
    }

    private static boolean isBomb(List<Integer> list) {
        return Objects.equals(list.get(0), list.get(1)) && Objects.equals(list.get(1), list.get(2)) && Objects.equals(list.get(2), list.get(3));
    }

    private static boolean isThreeOfOneKind(List<Integer> list) {
        return Objects.equals(list.get(0), list.get(1)) && Objects.equals(list.get(1), list.get(2));
    }

    private static boolean isPair(List<Integer> list) {
        return Objects.equals(list.get(0), list.get(1));
    }
}
